1.10 异常处理

作为一个健壮的后端程序,必须要有健壮的返回信息,动不动就返回一个html页面内容是一种令人作呕的用户体验。通过全局异常处理可以确保不论正常或者异常都可以一统一的数据格式响应,同时也规范了异常处理机制。
SpringBoot之需要采用注解@ExceptionHandler就可以实现全局的异常处理。首先我们需要定义一个状态返回码的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

/**
* ResponseCode
* 响应编码
*
* @author: zone7
* @time: 2018.08.28
*/
public enum ResponseCode {

//响应成功编码
SUCCESS(200, "请求成功"),


//响应错误编码
ERROR(40000, "请求错误"),
ERROR_PARAM_ILLGAL(40001, "请求参数不合法"),
ERROR_DATA_DUPLICATION(40002, "新增数据已存在"),
ERROR_GET_NO_RESULT(40003, "查询结果为空"),
ERROR_ADD_OP(40004, "新增操作异常"),
ERROR_UPDATE_OP(40005, "更新操作异常"),
ERROR_DELETE_OP(40006, "删除操作异常"),
ERROR_RECOVER_OP(40007, "恢复操作异常"),
ERROR_MODIFY_PASSWORD(40008, "修改密码操作异常"),
ERROR_CONFIG(40009, "系统配置异常"),

ERROR_LOGIN_NOAUTH(50000, "用户未登录,请登录后继续访问"),
ERROR_LOGIN_NOAUTH_ACL(50001, "用户没有该功能权限");

private final int code;
private final String desc;

ResponseCode(int code, String desc) {
this.code = code;
this.desc = desc;
}

public int getCode() {
return code;
}

public String getDesc() {
return desc;
}

}

接着我们需要统一定义一个响应类ResponseData作为所有接口的返回对象,所有控制层的返回值都必须是ResponseData类型,响应类的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

/**
* ResponseData
* 通用服务端响应对象
*
* @author: zone7
* @time: 2018.08.28
*/
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class ResponseData<T> implements Serializable {

/**
* 响应编码
*/
private int code;

/**
* 响应信息
*/
private String message;

/**
* 响应数据
*/
private T data;

private ResponseData(int code) {
this.code = code;
}

private ResponseData(int code, T data) {
this.code = code;
this.data = data;
}

private ResponseData(int code, String msg) {
this.code = code;
this.message = msg;
}

private ResponseData(int code, String msg, T data) {
this.code = code;
this.message = msg;
this.data = data;
}

public boolean isSuccess() {
return this.code == ResponseCode.SUCCESS.getCode();
}

public int getCode() {
return code;
}

public String getMessage() {
return message;
}

public T getData() {
return data;
}

/************************ 响应成功返回对象 ***************************/
public static <T> ResponseData<T> success() {
return new ResponseData<T>(ResponseCode.SUCCESS.getCode());
}

public static <T> ResponseData<T> success(T data) {
return new ResponseData<T>(ResponseCode.SUCCESS.getCode(), data);
}

public static <T> ResponseData<T> success(String message, T data) {
return new ResponseData<T>(ResponseCode.SUCCESS.getCode(), message, data);
}

public static <T> ResponseData<T> successMessage(String message) {
return new ResponseData<T>(ResponseCode.SUCCESS.getCode(), message);
}
/************************ 响应成功返回对象 ***************************/


/************************ 响应错误异常返回对象 ***************************/
public static <T> ResponseData<T> error() {
return new ResponseData<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc());
}

public static <T> ResponseData<T> error(ResponseCode responseCode) {
return new ResponseData<T>(responseCode.getCode(), responseCode.getDesc());
}

public static <T> ResponseData<T> error(String message, T data) {
return new ResponseData<T>(ResponseCode.ERROR.getCode(), message, data);
}

public static <T> ResponseData<T> errorMessage(String message) {
return new ResponseData<T>(ResponseCode.ERROR.getCode(), message);
}
/************************ 响应错误异常返回对象 ***************************/


/************************ 响应其他异常返回对象 ***************************/
public static <T> ResponseData<T> error(int code, String message) {
return new ResponseData<T>(code, message);
}
/************************ 响应其他异常返回对象 ***************************/
}

接着我们需要定义一个全局异常基类GlobalException,GlobalException继承自运行时异常,同时定义一堆继承自全局异常类的异常类,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

/**
* GlobalException
*
* @author: zone7
* @time: 2019.02.14
*/
@Data
public class GlobalException extends RuntimeException {

private ResponseCode responseCode = ResponseCode.ERROR;

public GlobalException() {
}

public GlobalException(String message) {
super(message);
}

public GlobalException(String message, Throwable cause) {
super(message, cause);
}

public GlobalException(Throwable cause) {
super(cause);
}

protected GlobalException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

}

最后,我们将使用注解@ControllerAdvice和@ExceptionHandler配置一个统一的异常处理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

/**
* GlobalExceptionHandler
* 全局异常处理器
* 当请求处理出现异常时,会根据异常处理器的配置顺序,依次尝试异常匹配和处理
* 自定义异常处理器需要继承GlobalException类
*
* @author: zone7
* @time: 2019.02.14
*/
@ControllerAdvice
public class GlobalExceptionHandler {

// TODO: 添加自定义的异常处理器

@ExceptionHandler(value = DeleteOPException.class)
@ResponseBody
public ResponseData deleteExceptionHandler(HttpServletRequest request, DeleteOPException e) throws Exception {
return ResponseData.error(e.getResponseCode().getCode(), e.getMessage());
}

@ExceptionHandler(value = NoResultException.class)
@ResponseBody
public ResponseData noResultExceptionHandler(HttpServletRequest request, NoResultException e) throws Exception {
return ResponseData.error(e.getResponseCode().getCode(), e.getMessage());
}

@ExceptionHandler(value = DuplicationException.class)
@ResponseBody
public ResponseData duplicationExeptionHandler(HttpServletRequest request, DuplicationException e) throws Exception {
return ResponseData.error(e.getResponseCode().getCode(), e.getMessage());
}

@ExceptionHandler(value = ParamException.class)
@ResponseBody
public ResponseData paramExeptionHandler(HttpServletRequest request, ParamException e) throws Exception {
return ResponseData.error(e.getResponseCode().getCode(), e.getMessage());
}

/**
* 全局异常处理器
*
* @param request
* @param e
* @return
* @throws Exception
*/
@ExceptionHandler(value = GlobalException.class)
@ResponseBody
public ResponseData globalExceptionHandler(HttpServletRequest request, GlobalException e) throws Exception {
return ResponseData.error(e.getResponseCode().getCode(), e.getMessage());
}

/**
* 默认异常处理器
*
* @param request
* @param e
* @return
* @throws Exception
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResponseData exceptionHandler(HttpServletRequest request, Exception e) throws Exception {
String errorMsg = e.getMessage();
return ResponseData.errorMessage("[未知异常信息] " + errorMsg);
}
}

在统一一次处理类里面我们已经把所有自定义的异常和一些系统异常定义了处理方式,统一使用ResponseData返回错误信息,采用这种方式我们的前端或者客户端在访问接口过程中不论是否出现异常都可以得到相同的JSON格式返回。